/**
 * 响应式函数文件
 */

// 依赖收集类
class Dep { // dependence的缩写
    constructor() {
        this.subscribers = new Set(); // 订阅者集合, 通过Set去重
    }
    // 副作用函数收集的方法
    addEffect(effect) {
        this.subscribers.add(effect)
    }
    // 发布通知方法，执行所有订阅的副作用函数
    notify() {
        this.subscribers.forEach(effect => {
            effect()
        })
    }

    depend() {
        if (activeEffect) {
            this.addEffect(activeEffect)
        }
    }
}

const dep = new Dep(); // 实例化依赖收集对象

let activeEffect = null // 副作用临时存放变量
function watchEffect(effect) { // 副作用函数监听
    activeEffect = effect
    effect() // 默认执行一次副作用
    activeEffect = null // 依赖收集完后重置变量
}

// vue2 版本 Object.defineProperty 数据劫持, 对传入的对象的每个属性进行数据劫持
// function reactive(raw) { // raw一般代表原始数据
//     Object.keys(raw).forEach(key => {
//         const dep = getDep(raw, key)
//         let value = raw[key]
//         Object.defineProperty(raw, key, {
//             get() {
//                 dep.depend() // 依赖收集
//                 return value
//             },
//             set(newValue) {
//                 if (value !== newValue) {
//                     value = newValue
//                     dep.notify() // 发布通知
//                 }
//             }
//         })
//     })
//     return raw
// }

// vue3版本 proxy
function reactive(raw) {
    return new Proxy(raw, {
        get(target, key, receiver) {
            const dep = getDep(target, key)
            dep.depend() // 依赖收集
            return Reflect.get(target, key, receiver)
        },
        set(target, key, newValue, receiver) {
            const dep = getDep(target, key)
            Reflect.set(target, key, newValue, receiver) // 设置最新值
            dep.notify() // 发布通知
        }
    });
}

// WeakMap 的key必须时一个对象，  Map的key是字符串
const targetMap = new WeakMap();
function getDep(target, key) {
    // 每个目标对象作为key存到弱引用的WeakMap中， 他的值对应的是一个Map对象，然后再通过Map对象来对目标对象的每个属性做对应的映射
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        depsMap = new Map();
        targetMap.set(target, depsMap)
    }
    // 取出具体的dep对象
    let dep = depsMap.get(key)
    if (!dep) {
        dep = new Dep()
        depsMap.set(key, dep)
    }
    return dep
}

// let info = reactive({counter: 1, name: 'test'})
// let test = reactive({counter2: 1})


// watchEffect(function () {
//     console.log("effecet1: ", info.counter, info.name)
// })

// watchEffect(function () {
//     console.log("effecet2: ", info.counter*info.counter)
// })

// watchEffect(function () {
//     console.log("effecet2: ", test.counter2)
// })

// info.counter ++
// info.name += 1